#include "card.h"
//#include "MsgTimer.h"
#include "TLVPacketer.h"
#include "Hall.h"
//#include "TLVPacketer.h"
#include "MsgTimer.h"
#include "Packet.h"


#include "ModernArtProcess.h"


int DataFlow::i = 0;
CMain  G_Main;


ostream &
operator << (ostream& out, const CCardSet &set)
{
    for (CCardSet::const_iterator it = set.begin(); it != set.end(); it++)
    {
        out << (*it)->Value << " ";
    }
    return out << endl;
}


void
CMsgSocket::MOnRecvMsg(unsigned int type, T_LParameter lp, const DataFlow &data)
{
	if (type == MSG_ON_TIMER)
		cout << lp << " time out " << endl;
}
void
CMsgSocket::Broadcast(struct sockaddr * broadaddr)
{
	EnterCriticalSection (&RoomMapLock);
	if ( RoomMap.empty() ) 
	{
		LeaveCriticalSection (&RoomMapLock);
		return;
	}
	TLVpacketer pa, room, roominfo;
	int roomnum = 0;
	for (map<string, T_RoomInfo>::iterator it = RoomMap.begin(); it != RoomMap.end(); it++)
	{
		roominfo[ P_Broadcast_User_ID ] = it->second.hostname;
		roominfo[ P_Broadcast_Room_Current ] = it->second.current;
		roominfo[ P_Broadcast_Room_Total ] = it->second.total;
		roominfo[ P_Broadcast_Room_ID ] = it->first;
		roominfo.MakePacket();
		room[roomnum] = roominfo;
		roomnum++;
	}
	room.MakePacket();
	pa[P_Broadcast_Server_Version] = 100;
	pa[P_Broadcast_Room_INFO_] = room;
	pa.MakePacket();

	LeaveCriticalSection (&RoomMapLock);

	if (broadaddr)
		sendto(sock, pa.GetData(), pa.GetLength(), 0, (sockaddr *)broadaddr, sizeof(sockaddr));
	else
	{
		struct sockaddr_in addr;
		addr.sin_family   =   AF_INET; 
		addr.sin_port   =   htons(5567); 
		addr.sin_addr.s_addr   =   htonl(INADDR_BROADCAST);
		sendto(sock, pa.GetData(), pa.GetLength(), 0, (sockaddr *)&addr, sizeof(sockaddr));
	}
}

void
CMsgSocket::BeginAccept()
{
    /*string command, arg;
    int price, gc, ci;
	int timer, id;
    TLVpacketer pa;*/
	int on = 1;
	struct sockaddr_in sin;

	if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
	  perror("socket");
	  exit(1);
	}
	if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
	  (char *)&on, sizeof(on)) < 0) {
	  perror("setsockopt");
	  exit(1);
	}
	sin.sin_family = PF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = htons(5566);
	if (bind(sock, (struct sockaddr *)&sin,
	  sizeof(sin)) < 0) {
	  perror("bind");
	  exit(1);
	}
	/* This sends the message to every machine
	   (port 5566) on the local subnet! */
	while(1)
	{
		SOCKADDR_STORAGE From;
		int FromLen, AmountRead;
        FromLen = sizeof (From);
		char readbuf[100] = {0};
        AmountRead = recvfrom(sock, readbuf, sizeof (readbuf), 0,
                              (LPSOCKADDR) & From, &FromLen);
		if (AmountRead > 0)
		{
			if ( strstr(readbuf, "get") )
				Broadcast( (LPSOCKADDR) & From );
		}
	}
	close(sock);


    /*while(1)
    {
        cout << "Command:" ;
        cin >> id >> timer;
		CMsgTimer::T_TimerData data = { timer, id };
		if (timer == 0)
			DelTimer( DataFlow(&data, sizeof(data)) );
		else
			SetTimer( DataFlow(&data, sizeof(data)) );
		continue;
        if ( command == "new" )
            Main->MPostMsg(MSG_TEST_SOCKET_ACCEPT, 0, sock);
        else
        {
            pa[PACKET_TYPE_SOCKET] = sock;
            pa[PACKET_TYPE_CONTENT] = command;
            if ( command == "join" )
            {
                cin >> arg;
                pa[PACKET_TYPE_HOST] = arg;
            }
            else if ( command == "game" )
            {
                while (1)
                {
                    cin >> arg;
                    if (arg == "end") break;
                    else if (arg == "price")
                    {
                        cin >> price;
                        pa[P_PRICE] = price;
                    }
                    else if (arg == "action")
                    {
                        cin >> gc;
                        pa[P_ACTION] = gc;
                    }
                    else if (arg == "command")
                    {
                        cin >> gc;
                        pa[P_COMMAND] = gc;
                    }
                    else if (arg == "card")
                    {
                        char card[10] = {0};
                        unsigned int i = 0;

                        while (1)
                        {
                            cin >> ci;
                            if (ci == -1) break;
                            card[i++] = ci;
                        }
                        pa[P_CARD] = DataFlow(card, i);
                    }
                }
            }
            pa.MakePacket();
            Main->MPostMsg(MSG_TEST_SOCKET_RECV, 0, pa);
        }
    }*/
}

void
CGameInstance::SetRoom(int roomid, string hostname, unsigned char current, unsigned char total)
{
	Broad->SetRoom(num2str(roomid), hostname, current, total);
}

void
CMain::MOnRecvMsg(unsigned int type, T_LParameter lp, const DataFlow &data)
{
    string sock, command, arg, name;
    TLVpacketer pa;
    CGameInstance *game;
    CUser *user;
    int com, socket;
	CUser::T_UserUniqueID userid;
	CGameInstance::T_InstanceID roomid;

    switch (type)
    {
    case MSG_TEST_SOCKET_ACCEPT:
        sock = data.GetData();
        //string sock(data.GetData());
        if ( MAP_NOT_CONTAIN(Users, sock) )
        {
            user = new CUser(sock);
            Users[sock] = user;
            user->StartMsgThread();
            cout << "User " << sock << " login" << endl;
        }
        break;
    case MSG_TEST_SOCKET_RECV:
        pa = data;
        sock = pa[PACKET_TYPE_SOCKET].GetData();
        command = pa[PACKET_TYPE_CONTENT].GetData();
        if ( MAP_NOT_CONTAIN(Users, sock) )
            break;
        user = Users[sock];
        if (command == "create")
        {
            if ( MAP_NOT_CONTAIN(Instances, sock) )
            {
                game = new CGameInstance(new CModernArtProcess);
                Instances[sock] = game;
                game->StartMsgThread();
                user->JoinGame(game->GetMD(), 1);
            }
        }
        else if (command == "start")
        {
            if ( !MAP_NOT_CONTAIN(Instances, sock) )    // only host can start game
            {
                user->StartGame();
            }
        }
        else if (command == "join")
        {
            arg = pa[PACKET_TYPE_HOST].GetData();
            if ( !MAP_NOT_CONTAIN(Instances, arg) )
            {
                game = Instances[arg];
                user->JoinGame(game->GetMD(), 0);
            }
        }
        else if (command == "game")
        {
            POST_MSG_TO(user->Room, MSG_GAME_COMMAND, user->GetMD(), data);
        }
        break;
    case MSG_SOCKET_RECV:
        pa = data;
        socket = lp;	
		userid = socket;
        if (pa[P_USER_COMMAND].GetNumber(com))
        {
            if ( MAP_NOT_CONTAIN(Users, userid) )
                break;
            user = Users[userid];
			switch (com)
			{
			case P_USER_COMMAND_LOGIN_QUICKJOIN:
			case P_USER_COMMAND_LOGIN:
			case P_USER_COMMAND_LOGIN_JOIN:
			case P_USER_COMMAND_LOGIN_CREATE:
				if (user) return;
				name = pa[P_USER_NAME].GetData();
				if (name == "") break;
				//string sock(data.GetData());
				user = new CUser(name, socket);
				Users[userid] = user;
				user->StartMsgThread();
				printf("%s: User %s login from %d\n",
				  "ServerMsgDealer", name.c_str(), lp);
				if (com == P_USER_COMMAND_LOGIN_CREATE) goto gt_create;
				else if (com == P_USER_COMMAND_LOGIN_JOIN ) goto gt_join;
				else if (com == P_USER_COMMAND_LOGIN_QUICKJOIN ) goto gt_quickjoin;
				else
				break;
			case P_USER_COMMAND_CREATE:
gt_create:			if (!user) return;
				//roomid = userid;
               // if ( MAP_NOT_CONTAIN(Instances, roomid) )
                //{
					if (user->Room != -1) return;
                    game = new CGameInstance(new CModernArtProcess);
					roomid = num2str ( game->GetMD() );
					printf("%s room opened by %s\n", roomid.c_str(), user->UserName.c_str());
                    Instances[roomid] = game;
					game->Broad = &MsgSocket;
					game->Hall = GetMD();
                    game->StartMsgThread();
                    user->JoinGame(game->GetMD(), 1);
					//MsgSocket.SetRoom(roomid, user->UserName, 1, 5);
                //}
				break;
			case P_USER_COMMAND_START:
				if (!user) return;
				roomid = num2str ( user->Room );
                if ( !MAP_NOT_CONTAIN(Instances, roomid) )    
                {
                    user->StartGame();   // only host can start game 
                }
				break;
			case P_USER_COMMAND_QUICK_JOIN:
gt_quickjoin:		if (!user) return;
				if (Instances.size() > 0)
				{
					game = Instances.rbegin()->second;
					user->JoinGame(game->GetMD(), 0);
				}
				break;
			case P_USER_COMMAND_JOIN:
gt_join:		if (!user) return;
                roomid = pa[P_USER_ROOM_NAME].GetData();
                if ( !MAP_NOT_CONTAIN(Instances, roomid) )
                {
                    game = Instances[roomid];
                    user->JoinGame(game->GetMD(), 0);
                }
				break;
			case P_USER_COMMAND_GAME:
				if (!user) return;
				if (user->Room != -1)
					POST_MSG_TO(user->Room, MSG_GAME_COMMAND, user->GetMD(), data);
				break;
			case P_USER_COMMAND_MSG:
				if (!user) return;
				if (user->Room != -1)
					POST_MSG_TO(user->Room, MSG_ROOM_MSG, user->GetMD(), data);
				break;
			case P_USER_COMMAND_EXIT:
				if (!user) return;
                user->Exit(); 
				user->DeleteClass();
				Users[userid] = NULL;
				printf("%s: User exit\n", user->UserName.c_str());
				break;
			}
        }
       // printf("%s: Recev on socket %d, length %d\ncontent:%s\n",
                  // "ServerMsgDealer", lp, data.GetLength(), data.GetData());
        break;
    case MSG_SOCKET_NEW:
        socket = lp;	
		userid = socket;
		if ( MAP_NOT_CONTAIN(Users, userid) )
		{
			Users[userid] = NULL;
			printf("%s: New connection on socket %d\n",
               "ServerMsgDealer", lp);
		}
        break;
    case MSG_SOCKET_HUNGUP:
        socket = lp;	
		userid = socket;
        //string sock(data.GetData());
        if ( !MAP_NOT_CONTAIN(Users, userid) )
        {
            user = Users[userid];
            Users.erase(userid);
			if (user)
			{
				 user->Exit(); 
				 user->DeleteClass();
			}
        }
        printf("%s: socket %d hung up\n", "ServerMsgDealer", lp);
        break;
    case MSG_SOCKET_ERROR:
        printf("%s: socket error %s\n", "ServerMsgDealer", data.GetData());
        break;
	case MSG_ROOM_DELETE:
		roomid = num2str(lp);
		if ( !MAP_NOT_CONTAIN(Instances, roomid) )    
        {
			Instances[roomid]->DeleteClass();  
			Instances.erase(roomid);
			printf("%s room closed\n", roomid.c_str());
        }
    }
}

void main()
{
    SOCKETINIT
	GetMsgTimer()->StartMsgThread();
    G_Main.StartMsgThread();
    G_Main.Socket.RegisterMsg(G_Main.GetMD());
    G_Main.Socket.ThreadStart();
    Sleep(100);
    G_Main.MsgSocket.Init(&G_Main);
	G_Main.MsgSocket.StartMsgThread();
    G_Main.MsgSocket.BeginAccept();
    SOCKETFINISH
}